<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Node类型</title>
  </head>
  <body>
    <div id="app">
      <div class="container">
        <div class="content">
          <p>Hello world!</p>
          <p>Hello DOM</p>
        </div>
      </div>
      <p>底部元素</p>
    </div>
  </body>
  <script>
    /** 1.Node类型常量比较
     * **/
    /**
     * @Author: ZHOU
     * @Date: 2021-10-28 15:39:58
     * @Eevent:
     * @Description: 节点类型可通过与这些Node类型定义的常量比较来确定
     * @Return: ''
     * @param {*} id
     */
    function compareNode(id) {
      let dom = document.getElementById(id);
      if (dom.nodeType === Node.ELEMENT_NODE) {
        console.log(
          "🚀 ~ file: 14.1.1 Node 类型.html ~ line 19 ~ compareNode ~ Node.ELEMENT_NODE",
          Node.ELEMENT_NODE
        ); //1
        console.log(
          "🚀 ~ file: 14.1.1 Node 类型.html ~ line 19 ~ compareNode ~ dom.nodeType",
          dom.nodeType
        ); //1
        console.log("Node is an element");
      }
    }
    /**
     * @Author: ZHOU
     * @Date: 2021-10-28 16:03:38
     * @Eevent:
     * @Description: nodeName与nodeValue
     * @Return: ''
     */
    compareNode("app");
    //例子比较了 someNode.nodeType 与 Node.ELEMENT_NODE 常量。如果两者相等，则意味着someNode 是一个元素节点。

    /** 2.nodeName 与 nodeValue
     * nodeName 与 nodeValue 保存着有关节点的信息。这两个属性的值完全取决于节点类型。在使用这两个属性前，最好先检测节点类型。
     * **/
    function checkNodeType(id) {
      let dom = document.getElementById(id);
      if (dom.nodeType === 1) {
        console.log("元素节点名称：" + dom.nodeName); //元素节点名称：DIV
        console.log("元素节点内容：" + dom.nodeValue); //元素节点内容：null
      }
    }
    checkNodeType("app");
    //在这个例子中，先检查了节点是不是元素。如果是，则将其 nodeName 的值赋给一个变量。对元素而言，nodeName 始终等于元素的标签名，而 nodeValue 则始终为 null。

    /** 3.节点关系（childNodes，NodeList）
     *   a.文档中的所有节点都与其他节点有关系。这些关系可以形容为家族关系，相当于把文档树比作家谱。在 HTML 中，<body>元素是<html>元素的子元素，
     *     而<html>元素则是<body>元素的父元素。<head>元素是<body>元素的同胞元素，因为它们有共同的父元素<html>。
     *   b.每个节点都有一个 childNodes 属性，其中包含一个 NodeList 的实例。NodeList 是一个类数组对象，用于存储可以按位置存取的有序节点。
     *   c.NodeList 对象独特的地方在于，它其实是一个对 DOM 结构的查询，因此 DOM 结构的变化会自动地在 NodeList 中反映出来。我们通常说 NodeList 是
     *     实时的活动对象，而不是第一次访问时所获得内容的快照。
     *   d.Node.childNodes 返回包含指定节点的子节点的集合（记住是子节点，不是孙子节点），该集合为即时更新的集合（live collection）。
     *
     * **/
    /**
     * @Author: ZHOU
     * @Date: 2021-10-28 23:31:19
     * @Eevent: 
     * @Description: ''
     * @Return: ''
     */    
    function checkNodeList() {
      let dom = document.getElementById("app");
      console.log(
        "🚀 ~ file: 14.1.1 Node 类型.html ~ line 74 ~ checkNodeList ~ dom.childNodes",
        dom.childNodes
      ); //获取app所有的子节点
      console.log(
        "🚀 ~ file: 14.1.1 Node 类型.html ~ line 76 ~ checkNodeList ~ dom.childNodes.item",
        dom.childNodes.item(1)
      ); //获取app的第二个子节点
      console.log(
        "🚀 ~ file: 14.1.1 Node 类型.html ~ line 76 ~ checkNodeList ~ dom.childNodes.length",
        dom.childNodes.length
      ); //获取app的子节点数量
    }
    checkNodeList();
    //无论是使用中括号还是 item()方法都是可以的，但多数开发者倾向于使用中括号，因为它是一个类数组对象。

    /** 4.parentNode、previousSibling、nextSibling属性
     *    a.每个节点都有一个 parentNode 属性，指向其 DOM 树中的父元素。
     *    b.childNodes 中的所有节点都有同一个父元素，因此它们的 parentNode 属性都指向同一个节点。
     *    c.此外，childNodes 列表中的每个节点都是同一列表中其他节点的同胞节点。
     *    d.而使用 previousSibling 和 nextSibling 可以在这个列表的节点间导航。这个列表中第一个节点的 previousSibling
     *      属性是 null，最后一个节点的nextSibling 属性也是 null，
     *    e.注意，如果 childNodes 中只有一个节点，则它的 previousSibling 和 nextSibling 属性都是null。
     * **/
     function checkParentNode(){
      let father = document.getElementById("app");//最外层父元素;
      let firstSon = father.childNodes[0];//最外层父元素的第一个子元素；
      let lastSon = father.childNodes[father.childNodes.length - 1];
      console.log('parentNode--:',firstSon.parentNode === father);//parentNode--: true
      console.log('previousSibling--:',firstSon.previousSibling);//previousSibling--: null
      console.log('parentNode--:',lastSon.parentNode === father);//parentNode--: true
      console.log('nextSibling--:',lastSon.nextSibling);//nextSibling--: null
      console.log('firstSon.nextSibling === father.childNodes[1]',firstSon.nextSibling === father.childNodes[1]);//firstSon.nextSibling === father.childNodes[1] true
     }
     checkParentNode();

     /** 5.firstChild、lastChild
      *    a.父节点和它的第一个及最后一个子节点也有专门属性：firstChild 和 lastChild 分别指向childNodes 中的第一个和最后一个子节点。
      *    b.someNode.firstChild 的值始终等于 someNode.childNodes[0]。
      *    c.someNode.lastChild 的值始终等于 someNode.childNodes[someNode.childNodes.length-1]。
      *    e.如果只有一个子节点，则 firstChild 和 lastChild 指向同一个节点。如果没有子节点，则 firstChild 和 lastChild 都是 null。
      * **/

      /** 6.hasChildNodes()与childNodes.length
       *    检查父元素是否有子元素
       * **/
       function checkHasChild(){
         let father = document.getElementById('app');
         console.log(father.hasChildNodes());//true 这个方法如果返回 true 则说明节点有一个或多个子节点。
         console.log(father.childNodes.length);//5
       }
       checkHasChild();

       /** 7.ownerDocument
        *    a.ownerDocument 属性是一个指向代表整个文档的文档节点的指针。所有节点都被创建它们（或自己所在）的文档所拥有，因为一个节点不可能同时存在于两个或
        *     者多个文档中。这个属性为迅速访问文档节点提供了便利，因为无需在文档结构中逐层上溯了。
        * **/
       function checkownerDocument(){
         let father = document.getElementById('app');
         console.log('father.ownerDocument === father.firstChild.ownerDocument',father.ownerDocument === father.firstChild.ownerDocument);//father.ownerDocument === father.firstChild.ownerDocument true
       }
       checkownerDocument();

       /** 8.虽然所有节点类型都继承了 Node，但并非所有节点都有子节点。本章后面会讨论不同节点类型的差异
       */
  </script>
</html>
